ArcBall Rotation Kontrolle
von Terence J. Grant (tjgrant@tatewake.com.)
Wäre es nicht großartig, wenn Sie Ihr Modell rotieren lassen könnten, indem Sie nur die Maus verwenden? Mit einem ArcBall können Sie das machen. In diesem Dokument komme ich auf meine Implementation und Betrachtungen zurück, damit Sie sowas in Ihren eigenen Projekten einfügen können.
Meine Implementation der ArcBall Klasse basiert auf Bretton Wade's, welche wiederum auf Ken Shoemake's, aus der Graphic Gems Buchreihe, basiert. Wie dem auch sei. Ich habe ein paar Fehler behoben und einige Optimierungen für unser Vorhaben vorgenommen.
Der ArBall funktioniert so, dass die Klick-Koordinaten in einem Fenster direkt in di ArcBall Sphere Koordinaten gemapped werden, als ob es direkt vor Ihnen wäre.
Um das zu erreichen, skalieren wir einfach die Maus-Koordinaten herunter, in eine Spann von [0...width), [0...height) bis [-1...1], [1...-1] – (Behalten Sie im Hinterkopf, dass wir das Vorzeichen von Y ändern, so dass wir korrekte Ergebnisse in OpenGL erhalten.) Und das sieht wie folgt aus:
MousePt.X = ((MousePt.X / ((Width – 1) / 2)) – 1); MousePt.Y = -((MousePt.Y / ((Height – 1) / 2)) – 1);
ArcBall_t::ArcBall_t(GLfloat NewWidth, GLfloat NewHeight)
void ArcBall_t::click(const Point2fT* NewPt)
void ArcBall_t::drag(const Point2fT* NewPt, Quat4fT* NewRot)
void ArcBall_t::setBounds(GLfloat NewWidth, GLfloat NewHeight)
// Finale Transformation
Matrix4fT Transform = { 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f };
Matrix3fT LastRot = { 1.0f, 0.0f, 0.0f, // Letzte Rotation
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f };
Matrix3fT ThisRot = { 1.0f, 0.0f, 0.0f, // DieseRotation
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f };
ArcBallT ArcBall(640.0f, 480.0f); // ArcBall Instanz
Point2fT MousePt; // aktueller Maus Punkt
bool isClicked = false; // wurde die Maus geklickt?
bool isRClicked = false; // wurde die rechte Maustaste geklickt?
bool isDragging = false; // wird die Maus (mit gehaltener Taste) gezogen?
void ReshapeGL (int width, int height)
{
. . .
ArcBall.setBounds((GLfloat)width, (GLfloat)height); // aktualisier Mausgrenze für ArcBall
}
// bearbeite Fenster Nachrichten
LRESULT CALLBACK WindowProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
. . .
// Maus basierte Nachrichten für ArcBall
case WM_MOUSEMOVE:
MousePt.s.X = (GLfloat)LOWORD(lParam);
MousePt.s.Y = (GLfloat)HIWORD(lParam);
isClicked = (LOWORD(wParam) & MK_LBUTTON) ? true : false;
isRClicked = (LOWORD(wParam) & MK_RBUTTON) ? true : false;
break;
case WM_LBUTTONUP: isClicked = false; break;
case WM_RBUTTONUP: isRClicked = false; break;
case WM_LBUTTONDOWN: isClicked = true; break;
case WM_RBUTTONDOWN: isRClicked = true; break;
. . .
}
if (isRClicked) // Wenn die rechte Maustaste geklickt wird, setze alle Rotationen zurück
{
// setze Rotation zurück
Matrix3fSetIdentity(&LastRot);
// Resette Rotation
Matrix3fSetIdentity(&ThisRot);
// Resette Rotation
Matrix4fSetRotationFromMatrix3f(&Transform, &ThisRot);
}
if (!isDragging) // Kein Ziehen der Maus mit gehaltener Taste
{
if (isClicked) // erster Klick
{
isDragging = true; // bereite auf's Ziehen vor
LastRot = ThisRot; // Setze die letzte statische Rotation auf die letzte dynamische
ArcBall.click(&MousePt); // aktualisiere Anfangsvektor und bereite auf's Ziehen vor
}
}
else
{
if (isClicked) // Taste wird immer noch geklickt, also immer noch gezogen
{
Quat4fT ThisQuat;
ArcBall.drag(&MousePt, &ThisQuat); // aktualisiere Endvektor und ermittle Rotation als Quarternion
Matrix3fSetRotationFromQuat4f(&ThisRot, &ThisQuat); // konvertiere Quaternion in Matrix3fT
Matrix3fMulMatrix3f(&ThisRot, &LastRot); // akkumuliere letzte Rotation in die aktuelle
Matrix4fSetRotationFromMatrix3f(&Transform, &ThisRot); // Setze unsere endgültige Transformations Rotation von dieser
}
else // Es wird nicht länger gezogen
isDragging = false;
}
glPushMatrix(); // bereite dynamische Transformation vor glMultMatrixf(Transform.M); // wende dynamische Transformation an glBegin(GL_TRIANGLES); // fange an das Modell zu zeichnen . . . glEnd(); // fertig mit dem Zeichnen des Modells glPopMatrix(); // verwende keine dynamische Transformation mehr